﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Hawayel.RFC.Engine.DataStructures;
using System.Threading;
using System.IO;
using System.Threading.Tasks;

namespace Hawayel.RFC.Engine
{
    /// <summary>
    /// 
    /// </summary>
    public class Processor
    {
        /// <summary>
        /// 
        /// </summary>
        public int _progress = 0;

        /// <summary>
        /// 
        /// </summary>
        private static ReaderWriterLock _dirCreateLock = new ReaderWriterLock();

        /// <summary>
        /// 
        /// </summary>
        public static object ProgressLockObj = new object();

        /// <summary>
        /// 
        /// </summary>
        /// <param name="progress"></param>
        public delegate void ReportProgressDelegate(int progress);

        /// <summary>
        /// 
        /// </summary>
        public ReportProgressDelegate ReportProgress = null;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ps"></param>
        /// <returns></returns>
        public ErrorCode DoCopy(ProcessSettings ps)
        {
            try
            {
                //.
                var v = new Validator();
                var e = v.Validate(ps);

                if (e != ErrorCode.None)
                {
                    return e;
                }

                //.
                //.
                var rnd = new Random(GetSeed());
                var srcFiles = Directory.GetFiles(ps.SourceFolder, ps.FileTypes, ps.SearchOption).OrderBy(f => rnd.Next());

                var copyFiles = (ps.MaxFiles > 0) ? srcFiles.Take(ps.MaxFiles) : srcFiles;
                int copied = 0;

                //.
                Parallel.ForEach(copyFiles, f =>
                {

                    string relPath = GetRelativePath(f, ps.SourceFolder);
                    string destPath = CreateRelativePath(ps.DestinationFolder, relPath);
                    string dstFile = Path.Combine(destPath, Path.GetFileName(f));

                    File.Copy(f, dstFile, ps.OverwriteExisting);
                    lock (ProgressLockObj)
                    {
                        if (null != ReportProgress)
                        {
                            copied++;
                            int prog = (int)(100 * (decimal)copied / (decimal)ps.MaxFiles);
                            if (prog >= 1)
                            {
                                ReportProgress(prog);
                            }
                        }
                    }

                });

            }
            catch
            {
                return ErrorCode.Runtime;
            }

            return ErrorCode.None;
        }

        /// <summary>
        /// 
        /// </summary>
        private static int GetSeed()
        {

            return Environment.MachineName.GetHashCode() ^
                    Environment.OSVersion.GetHashCode() ^
                    Environment.TickCount.GetHashCode() ^
                    Environment.UserDomainName.GetHashCode() ^
                    Environment.UserName.GetHashCode() ^
                    Environment.WorkingSet.GetHashCode();
        }


        /// <summary>
        /// 
        /// </summary>
        private static string GetRelativePath(string file, string folder)
        {
            //.
            if (string.IsNullOrWhiteSpace(folder) || string.IsNullOrWhiteSpace(file))
            {
                return file;
            }

            //.
            if (false == folder.EndsWith(Path.DirectorySeparatorChar.ToString()))
            {
                folder = folder + Path.DirectorySeparatorChar;
            }

            //.
            if (false == file.StartsWith(folder, StringComparison.CurrentCultureIgnoreCase))
            {
                return file;
            }

            //.
            var fileName = Path.GetFileName(file);
            var relFilePath = file.Substring(folder.Length - 1);

            return relFilePath.Substring(0, relFilePath.LastIndexOf(fileName));

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="root"></param>
        /// <param name="child"></param>
        /// <returns></returns>
        private static string CreateRelativePath(string root, string child)
        {
            //.
            if (string.IsNullOrWhiteSpace(root) || string.IsNullOrWhiteSpace(child))
            {
                return root;
            }

            //.
            var current = root;

            foreach (var c in child.Split(Path.DirectorySeparatorChar).Where(d => false == string.IsNullOrWhiteSpace(d)))
            {
                var dir = Path.Combine(current, c);

                try
                {
                    _dirCreateLock.AcquireReaderLock(Timeout.Infinite);

                    if (false == Directory.Exists(dir))
                    {
                        LockCookie lc = _dirCreateLock.UpgradeToWriterLock(Timeout.Infinite);
                        try
                        {
                            Directory.CreateDirectory(dir);
                        }
                        finally
                        {
                            _dirCreateLock.DowngradeFromWriterLock(ref lc);
                        }
                    }
                }
                finally
                {
                    _dirCreateLock.ReleaseReaderLock();
                }
                current = dir;
            }

            return current;
        }

    }

}